package edu.gmu;

import edu.gmu.parser.Token;

import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

/**
 * ASTNode custom MutableTreeNode.
 * User: james
 * Date: 3/9/11
 * Time: 3:59 PM
 * To change this template use File | Settings | File Templates.
 */
public class ASTNode implements MutableTreeNode
{
    private TreeNode parent   = null;
    private List children     = new LinkedList();
    private Object userObject = null;
    private int mark          = -1; // position within stack

    /**
     * Creates a new ASTNode with default values.
     */
    public ASTNode( )
    {

    }

    /**
     * Creates a new ASTNode with specified values.
     * @param userObject
     */
    public ASTNode( Object userObject )
    {
        this.userObject = userObject;
    }

    public void addChildren(List newChildren)
    {
        for(int i = 0; i < newChildren.size(); i++)
        {
            ASTNode childNode = (ASTNode)newChildren.get(i);
            // Sanity check - can't add ourself as a child
            if( childNode == this )
            {
                String s = "Attempting to add node as child to itself.";
                throw new IllegalArgumentException(s);
            }
            this.add(childNode);
        }
    }

    public void add(MutableTreeNode childNode)
    {
        this.children.add(childNode);
        childNode.setParent(this);
    }

    //-----------------------------------------------------------------------//
    // Stack specific behavior - debatable location
    //-----------------------------------------------------------------------//
    public int getMark()
    {
        return this.mark;
    }

    public void setMark( int mark )
    {
        this.mark = mark;
    }


    //-----------------------------------------------------------------------//
    // Token specific behavior - debatable location
    //-----------------------------------------------------------------------//
    /**
     * Gets the token's image.
     * @return image
     */
    public String getImage( )
    {
        Token t = (Token)this.userObject;
        return t.image;
    }

    /**
     * Gets the token's value.
     * @return value
     */
    public Object getValue( )
    {
        Token t = (Token)this.userObject;
        return t.getValue();
    }

    /**
     * Gets the token's kind.
     * @return kind
     */
    public int getKind( )
    {
        Token t = (Token)this.userObject;
        return t.kind;
    }

    //-----------------------------------------------------------------------//
    // MutableTreeNode implementation
    //-----------------------------------------------------------------------//


    public void insert(MutableTreeNode childNode, int i)
    {
        this.children.add(i,childNode);
        childNode.setParent(this);
    }

    public void remove(int i)
    {
        this.children.remove(i);
    }

    public void remove(MutableTreeNode mutableTreeNode)
    {
        this.remove(mutableTreeNode);
    }

    public void setUserObject(Object userObject)
    {
        this.userObject = userObject;
    }

    // Not sure why this isn't part of TreeNode interface?
    public Object getUserObject()
    {
        return this.userObject;
    }

    public void removeFromParent()
    {
        MutableTreeNode ourParent = (MutableTreeNode)this.parent;
        ourParent.remove(this);
    }

    public void setParent(MutableTreeNode parent)
    {
        this.parent = parent;
    }

    //-----------------------------------------------------------------------//
    // TreeNode implementation
    //-----------------------------------------------------------------------//
    public TreeNode getChildAt(int i)
    {
        return (TreeNode)this.children.get(i);
    }

    public int getChildCount()
    {
        return this.children.size();
    }

    public TreeNode getParent()
    {
        return this.parent;
    }

    public int getIndex(TreeNode treeNode)
    {
        for(int i = 0; i < this.children.size(); i++)
        {
            Object child = this.children.get(i);
            if( child.equals(treeNode) == true )
            {
                return i;
            }
        }
        return -1;
    }

    public boolean getAllowsChildren()
    {
        return true;
    }

    public boolean isLeaf()
    {
        return (this.children.size() == 0);
    }

    public Enumeration children()
    {
        // Not supported
        return null;
    }

    /**
     * Determines if specified obj is equivalent to this object.
     * @param obj
     * @return true if this userObject is equal to obj.userObject
     */
    public boolean equals( Object obj )
    {
        if( obj == this ) return true;
        if( !(obj instanceof ASTNode ) )
        {
             return false;
        }
        ASTNode o = (ASTNode)obj;
        return (o.userObject==null)?
                this.userObject==null:o.userObject.equals(this.userObject);
    }

    public void print()
    {
        String s = null;
        if( this.userObject == null )
        {
            s = "null";
        }
        else
        {   Token t = (Token)this.userObject;
            s =  "image="+t.image+
                    " kind="+t.kind +
                    " value="+t.getValue();
        }
        System.out.println(s);
    }

    /**
     * String version of this object
     * @return string
     */
    public String toString()
    {


        if( this.userObject== null )
        {
            //return "userObject=null";
            return "null";
        }
        else
        {
            return this.userObject.toString();
        }
    }
}
